﻿using MvCameraControl;
using SDKHKApp.SDK;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace MyCamera
{
    /// <summary>
    /// 海康相机SDK二次封装类
    /// </summary>
    public class HKCamera : BaseCamera
    {
        #region 变量属性
        /// <summary>
        /// 用于根据枚举获取设备列表
        /// </summary>
        readonly DeviceTLayerType enumTLayerType = DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvUsbDevice
           | DeviceTLayerType.MvGenTLGigEDevice | DeviceTLayerType.MvGenTLCXPDevice | DeviceTLayerType.MvGenTLCameraLinkDevice | DeviceTLayerType.MvGenTLXoFDevice;
        /// <summary>
        /// 内存拷贝
        /// </summary>
        /// <param name="dest"></param>
        /// <param name="src"></param>
        /// <param name="count"></param>
        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
        /// <summary>
        /// 设备列表信息
        /// </summary>
        List<IDeviceInfo> deviceInfoList = new List<IDeviceInfo>();
        /// <summary>
        /// 设备编码集合
        /// </summary>
        List<string> deviceSNList = new List<string>();
        /// <summary>
        /// 设备
        /// </summary>
        IDevice device = null;
        IInterface _ifInstance;


        IntPtr m_BufForDriver = IntPtr.Zero;
        UInt32 _bufferSize = 3072 * 2048 * 3;
        private byte[] _buffer;
        private uint _buffSizeForSaveImage = 3072 * 2048 * (16 * 3 + 4) + 2048;
        private byte[] _bufForSaveImage;
        private Object m_BufForSaveImageLock = new Object();
        public IntPtr m_pSaveImageBuf = IntPtr.Zero;
        /// <summary>
        /// 保存图像锁
        /// </summary>
        private readonly object saveImageLock = new object();
        /// <summary>
        /// 获取到的帧信息, 用于保存图像
        /// </summary>
        private IFrameOut frameForSave;
        #endregion

        #region 初始化
        /// <summary>
        /// 海康相机操作类
        /// </summary>
        public HKCamera() : base() { }
        #endregion

        #region 设备操作
        /// <summary>
        /// 获取设备名称集合
        /// </summary>
        /// <returns></returns>
        public override List<string> GetListEnum()
        {
            List<string> deviceList = new List<string>();
            foreach (var item in GetListInfoEnum())
            {
                IDeviceInfo deviceInfo = item;
                if (deviceInfo.UserDefinedName != "")
                {
                    //deviceList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.UserDefinedName + " (" + deviceInfo.SerialNumber + ")");
                    deviceList.Add(deviceInfo.SerialNumber);
                }
                else
                {
                    //deviceList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.ManufacturerName + " " + deviceInfo.ModelName + " (" + deviceInfo.SerialNumber + ")");
                    deviceList.Add(deviceInfo.SerialNumber);
                }
            }
            return deviceList;
        }

        /// <summary>
        /// 通过提供相机的SN号连接设备
        /// </summary>
        /// <param name="CamSN"></param>
        /// <returns></returns>
        public override bool InitDevice(string CamSN)
        {
            try
            {
                int nRet;
                if (string.IsNullOrEmpty(CamSN)) return false;
                // 获取海康相机的序列化列表
                var infolist = GetListInfoEnum();
                if (infolist.Count < 1) return false;
                // 获取列表中第一台设备
                IDeviceInfo deviceInfo = infolist[0];
                bool selectSNflag = false;
                foreach (var item in infolist)
                {
                    if (item.SerialNumber.Equals(CamSN))
                    {
                        deviceInfo = item;
                        selectSNflag = true;
                        break;
                    }
                }
                if (!selectSNflag) return false;

                try
                {
                    // 通过查询到设备信息创建设备对象
                    device = DeviceFactory.CreateDevice(deviceInfo);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Create Device fail!" + ex.Message);
                    return false;
                }

                // 打开创建的设备
                nRet = device.Open();
                if (nRet != MvError.MV_OK)
                {
                    ShowErrorMsg("Open Device fail!", nRet);
                    return false;
                }

                // ch:注册回调函数 | en:Register image callback
                // 注册回调函数操作应放在打开采集流操作之前
                device.StreamGrabber.FrameGrabedEvent += CallBackEventHandler;

                #region 探测网络最佳包大小(只对GigE相机有效)
                if (device is IGigEDevice)
                {
                    int packetSize;
                    nRet = (device as IGigEDevice).GetOptimalPacketSize(out packetSize);
                    if (packetSize > 0)
                    {
                        nRet = device.Parameters.SetIntValue("GevSCPSPacketSize", packetSize);
                        if (nRet != MvError.MV_OK)
                        {
                            Console.WriteLine("Warning: Set Packet Size failed {0:x8}", nRet);
                        }
                        else
                        {
                            Console.WriteLine("Set PacketSize to {0}", packetSize);
                        }
                    }
                    else
                    {
                        Console.WriteLine("Warning: Get Packet Size failed {0:x8}", nRet);
                    }
                }
                #endregion


                // ch:设置触发模式为On || en:set trigger mode as On
                // 设置为Off会一直触发回调
                SetTriggerMode(TriggerMode.On, TriggerSource.Software);
                // 默认设置连续触发模式
                device.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous");

                if (nRet != MvError.MV_OK)
                {
                    Console.WriteLine("Set TriggerMode failed:{0:x8}", nRet);
                    return false;
                }

                if (!StartGrabbing())
                {
                    Console.WriteLine("开始采集失败");
                    return false;
                }

                //ch: 设置合适的缓存节点数量 | en: Setting the appropriate number of image nodes
                device.StreamGrabber.SetImageNodeNum(5);

                SN = CamSN;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 关闭设备
        /// </summary>
        /// <returns></returns>
        public override bool CloseDevice()
        {
            try
            {
                if (m_BufForDriver != IntPtr.Zero)
                {
                    Marshal.Release(m_BufForDriver);
                }

                // ch:关闭设备 | en:Close device
                var nRet = device.Close();
                if (MvError.MV_OK == nRet)
                {
                    // ch:销毁设备 | en:Destroy device
                    device.Dispose();
                    return true;
                }

                nRet = device.Close();
                if (MvError.MV_OK != nRet)
                {
                    Console.WriteLine("Close device failed:{0:x8}", nRet);
                    return false;
                }

                // ch:销毁设备 | en:Destroy device
                device.Dispose();
                return true;
            }
            catch
            {
                device.Dispose();
                device = null;
                return false;
            }
        }
        #endregion

        #region 图像采集
        /// <summary>
        /// ch:软触发执行一次 | en:Trigger once by software
        /// </summary>
        public override bool SoftTrigger()
        {
            int nRet;
            // ch:触发命令 | en:Trigger command
            nRet = device.Parameters.SetCommandValue("TriggerSoftware");

            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 开始采集
        /// </summary>
        /// <returns></returns>
        public override bool StartGrabbing()
        {
            // 抓取开始后设置默认状态
            // 关闭默认的实时模式
            // 0：实时
            // 1：触发器

            int nRet = device.StreamGrabber.StartGrabbing();
            if (MvError.MV_OK != nRet) Debug.WriteLine("Grab start failed");
            isGrabbing = true;
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 停止采集
        /// </summary>
        /// <returns></returns>
        public override bool StopGrabbing()
        {
            if (isRecord)
            {
                StopRecord(); // 停止录像
            }
            int nRet = device.StreamGrabber.StopGrabbing();
            if (MvError.MV_OK != nRet) Debug.WriteLine("Grab stop failed");
            isGrabbing = false;
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 开始录像
        /// </summary>
        public override bool StartRecord()
        {
            if (false == isGrabbing)
            {
                ShowErrorMsg("未开始采集", 0);
                return false;
            }

            IIntValue intValue;
            IEnumValue enumValue;
            IFloatValue floatValue;

            uint width; // 宽度
            uint height; // 高度
            MvGvspPixelType pixelType;
            float frameRate; // 帧率

            int result;

            result = device.Parameters.GetIntValue("Width", out intValue);
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("获取宽度失败!", result);
                return false;
            }
            width = (uint)intValue.CurValue;

            result = device.Parameters.GetIntValue("Height", out intValue);
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("获取高度失败!", result);
                return false;
            }
            height = (uint)intValue.CurValue;

            result = device.Parameters.GetEnumValue("PixelFormat", out enumValue);
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("获取像素格式失败!", result);
                return false;
            }
            pixelType = (MvGvspPixelType)enumValue.CurEnumEntry.Value;

            result = device.Parameters.GetFloatValue("ResultingFrameRate", out floatValue); // 获取帧率
            if (result == MvError.MV_OK)
            {
                ShowErrorMsg("获取帧率失败!", result);
                return false;
            }
            frameRate = floatValue.CurValue;

            // ch:开始录像 | en:Start record
            RecordParam recordParam;
            recordParam.Width = width;
            recordParam.Height = height;
            recordParam.PixelType = pixelType;
            recordParam.FrameRate = frameRate;
            recordParam.BitRate = 1000;
            recordParam.FormatType = VideoFormatType.AVI;

            result = device.VideoRecorder.StartRecord("./Record.avi", recordParam); // 开始录像
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("开始录像失败!", result);
                return false;
            }
            isRecord = true;
            return true;
        }

        /// <summary>
        /// 停止录像
        /// </summary>
        public override bool StopRecord()
        {
            if (false == isGrabbing)
            {
                ShowErrorMsg("未开始采集", 0);
                return false;
            }

            int result = device.VideoRecorder.StopRecord(); // 停止录像
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("停止录像失败!", result);
            }

            isRecord = false;
            return true;
        }
        #endregion

        #region 图像保存
        /// <summary>
        /// 保存Bmp图像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public override bool SaveBmp()
        {
            int result;

            try
            {
                ImageFormatInfo imageFormatInfo = new ImageFormatInfo(); // 图像格式信息
                imageFormatInfo.FormatType = ImageFormatType.Bmp; // 设置图像格式

                result = SaveImage(imageFormatInfo); // 保存图像
                if (result != MvError.MV_OK)
                {
                    ShowErrorMsg("保存图像失败!", result);
                    return false;
                }
                else
                {
                    ShowErrorMsg("保存图像成功!", 0);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("保存图像失败, " + ex.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 保存Jpg图像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public override bool SaveJpg()
        {
            int result;

            try
            {
                ImageFormatInfo imageFormatInfo; // 图像信息
                imageFormatInfo.FormatType = ImageFormatType.Jpeg; // 设置图像格式
                imageFormatInfo.JpegQuality = 80; // 图像质量

                result = SaveImage(imageFormatInfo); // 保存图像
                if (result != MvError.MV_OK)
                {
                    ShowErrorMsg("保存图像失败!", result);
                    return false;
                }
                else
                {
                    ShowErrorMsg("保存图像成功!", 0);
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("保存图像失败, " + ex.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 保存Png图像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public override bool SavePng()
        {
            int result;

            try
            {
                ImageFormatInfo imageFormatInfo = new ImageFormatInfo(); // 图像信息
                imageFormatInfo.FormatType = ImageFormatType.Png; // 设置图像格式

                result = SaveImage(imageFormatInfo); // 保存图像
                if (result != MvError.MV_OK)
                {
                    ShowErrorMsg("保存图像失败!", result);
                    return false;
                }
                else
                {
                    ShowErrorMsg("保存图像成功!", 0);
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("保存图像失败, " + ex.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 保存Tiff图像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public override bool SaveTiff()
        {
            int result;
            try
            {
                ImageFormatInfo imageFormatInfo = new ImageFormatInfo(); // 图像信息
                imageFormatInfo.FormatType = ImageFormatType.Tiff; // 设置图像格式

                result = SaveImage(imageFormatInfo); // 保存图像
                if (result != MvError.MV_OK)
                {
                    ShowErrorMsg("保存图像失败!", result);
                    return false;
                }
                else
                {
                    ShowErrorMsg("保存图像成功!", 0);
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("保存图像失败, " + ex.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 保存图像
        /// </summary>
        /// <param name="imageFormatInfo"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private int SaveImage(ImageFormatInfo imageFormatInfo)
        {
            if (frameForSave == null)
            {
                throw new Exception("没有可用图像");
            }

            string imagePath = $"Image_w{frameForSave.Image.Width.ToString()}_h{frameForSave.Image.Height.ToString()}_fn{frameForSave.FrameNum.ToString()}.{imageFormatInfo.FormatType.ToString()}";

            lock (saveImageLock)
            {
                return device.ImageSaver.SaveImageToFile(imagePath, frameForSave.Image, imageFormatInfo, CFAMethod.Equilibrated); // 保存图像
            }
        }
        #endregion

        #region 设置配置
        /// <summary>
        /// 设置触发模式及触发源
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="triggerEnum"></param>
        /// <returns></returns>
        public override bool SetTriggerMode(TriggerMode mode = TriggerMode.On, TriggerSource triggerEnum = TriggerSource.Software)
        {
            int nRet;
            switch (mode)
            {
                case TriggerMode.Off:
                    nRet = device.Parameters.SetEnumValueByString("TriggerMode", "Off");
                    break;
                case TriggerMode.On:
                    nRet = device.Parameters.SetEnumValueByString("TriggerMode", "On");
                    break;
                default:
                    nRet = device.Parameters.SetEnumValueByString("TriggerMode", "On");
                    break;
            }
            bool flag1 = (MvError.MV_OK == nRet);
            switch (triggerEnum)
            {
                case TriggerSource.Software:
                    nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Software");
                    break;
                case TriggerSource.Line0:
                    nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line0");
                    break;
                case TriggerSource.Line1:
                    nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line1");
                    break;
                case TriggerSource.Line2:
                    nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line2");
                    break;
                case TriggerSource.Line3:
                    nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line3");
                    break;
                default:
                    nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line4");
                    break;
            }
            bool flag2 = (MvError.MV_OK == nRet);
            return flag1 && flag2;
        }

        /// <summary>
        /// 获取触发模式及触发源
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source)
        {
            int nRet;
            mode = TriggerMode.On;
            source = TriggerSource.Software;

            IEnumValue enumValue;
            nRet = device.Parameters.GetEnumValue("TriggerMode", out enumValue);
            bool flag1 = (MvError.MV_OK == nRet);
            if (flag1)
            {
                switch (enumValue.CurEnumEntry.Symbolic)
                {
                    case "On":
                        mode = TriggerMode.On;
                        break;
                    case "Off":
                        mode = TriggerMode.Off;
                        break;
                    default:
                        mode = TriggerMode.On;
                        break;
                }
            }

            nRet = device.Parameters.GetEnumValue("TriggerSource", out enumValue);
            bool flag2 = (MvError.MV_OK == nRet);
            if (flag2)
            {
                switch (enumValue.CurEnumEntry.Symbolic)
                {
                    case "TriggerSoftware":
                        source = TriggerSource.Software;
                        break;
                    case "Line0":
                        source = TriggerSource.Line0;
                        break;
                    case "Line1":
                        source = TriggerSource.Line1;
                        break;
                    case "Line2":
                        source = TriggerSource.Line2;
                        break;
                    default:
                        source = TriggerSource.Line0;
                        break;
                }
            }
            return flag1 && flag2;
        }

        /// <summary>
        /// 设置曝光时间
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool SetExpouseTime(float value)
        {
            int nRet = device.Parameters.SetFloatValue("ExposureTime", value);
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 获取曝光时间
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool GetExpouseTime(out float value)
        {
            IFloatValue floatValue;
            int nRet = device.Parameters.GetFloatValue("ExposureTime", out floatValue);
            value = (float)floatValue.CurValue;
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 设置增益
        /// </summary>
        /// <param name="gain"></param>
        /// <returns></returns>
        public override bool SetGain(float gain)
        {
            int nRet = device.Parameters.SetFloatValue("Gain", (float)gain);
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 获取增益
        /// </summary>
        /// <param name="gain"></param>
        /// <returns></returns>
        public override bool GetGain(out float gain)
        {
            gain = 0;
            IFloatValue floatValue;
            int nRet = device.Parameters.GetFloatValue("Gain", out floatValue);
            gain = (float)floatValue.CurValue;
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 获取帧率
        /// </summary>
        /// <returns></returns>
        public override bool GetFrameRate(out float frameRate)
        {
            frameRate = 0;
            IFloatValue floatValue;
            int nRet = device.Parameters.GetFloatValue("ResultingFrameRate", out floatValue); // 获取帧率
            frameRate = (float)floatValue.CurValue;
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 设置帧率
        /// </summary>
        /// <param name="gain"></param>
        /// <returns></returns>
        public override bool SetFrameRate(float frameRate)
        {
            var nRet = device.Parameters.SetBoolValue("AcquisitionFrameRateEnable", true); // 设置采集帧启用
            if (nRet != MvError.MV_OK)
            {
                ShowErrorMsg("设置采集帧启用失败!", nRet);
            }
            else
            {
                nRet = device.Parameters.SetFloatValue("AcquisitionFrameRate", frameRate); // 设置采集帧率
                if (nRet != MvError.MV_OK)
                {
                    ShowErrorMsg("设置采集帧率失败!", nRet);
                }
            }
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 获取像素格式
        /// </summary>
        /// <returns></returns>
        public override bool GetPixelFormat(out List<string> pixelFormats) 
        {
            pixelFormats = new List<string>();
            IEnumValue enumValue;
            var nRet = device.Parameters.GetEnumValue("PixelFormat", out enumValue); // 获取像素格式
            if (nRet == MvError.MV_OK)
            {
                foreach (var item in enumValue.SupportEnumEntries) // 循环添加帧率
                {
                    pixelFormats.Add(item.Symbolic);
                }
            }
            return (MvError.MV_OK == nRet);
        }

        public override bool SetTriggerPolarity(TriggerPolarity polarity)
        {
            IEnumValue enumValue;
            int nRet = _ifInstance.Parameters.GetEnumValue("TimerTriggerActivation", out enumValue);
            if (nRet != MvError.MV_OK)
            {
                return false;
            }
            for (int i = 0; i < enumValue.SupportedNum; ++i)
            {

                if (polarity.ToString().Equals(enumValue.SupportEnumEntries[i].Symbolic, StringComparison.OrdinalIgnoreCase))
                {
                    nRet = _ifInstance.Parameters.SetEnumValue("TimerTriggerActivation", enumValue.SupportEnumEntries[i].Value);
                    if (nRet != MvError.MV_OK)
                    {
                        return false;
                    }

                    break;
                }
            }
            return (MvError.MV_OK == nRet);
        }

        public override bool GetTriggerPolarity(out TriggerPolarity polarity)
        {
            polarity = TriggerPolarity.RisingEdge;
            IEnumValue enumValue;
            int nRet = _ifInstance.Parameters.GetEnumValue("TimerTriggerActivation", out enumValue);
            if (nRet != MvError.MV_OK)
            {
                return false;
            }
            string activate = enumValue.CurEnumEntry.Symbolic;
            //1下降沿 0 上升沿
            if (activate == "RisingEdge")
            { //上升沿
                polarity = TriggerPolarity.RisingEdge;
            }
            else if (activate == "FallingEdge")
            { //下降沿
                polarity = TriggerPolarity.FallingEdge;
            }
            return (MvError.MV_OK == nRet);
        }

        public override bool SetTriggerFliter(float flitertime)
        {
            int nRet = _ifInstance.Parameters.SetIntValue("LineDebouncerTimeNs", (uint)flitertime);
            nRet = _ifInstance.Parameters.SetIntValue("LineDebouncerTime", (uint)flitertime);
            return (MvError.MV_OK == nRet);
        }

        public override bool GetTriggerFliter(out float flitertime)
        {
            flitertime = 1000;
            IFloatValue floatValue;
            int nRet = device.Parameters.GetFloatValue("ExposureTime", out floatValue);
            flitertime = (float)floatValue.CurValue;
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 设置触发延迟
        /// </summary>
        /// <param name="delay"></param>
        /// <returns></returns>
        public override bool SetTriggerDelay(float delay)
        {
            int nRet = device.Parameters.SetFloatValue("TriggerDelay", delay);
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 获取触发延迟
        /// </summary>
        /// <param name="delay"></param>
        /// <returns></returns>
        public override bool GetTriggerDelay(out float delay)
        {
            delay = 0;
            IFloatValue floatValue;
            int nRet = device.Parameters.GetFloatValue("TriggerDelay", out floatValue);
            delay = (float)floatValue.CurValue;
            return (MvError.MV_OK == nRet);

        }

        public override bool SetLineMode(IOLines line, LineMode mode)
        {
            int nRet = device.Parameters.SetEnumValueByString(line.ToString(), mode.ToString());
            return (MvError.MV_OK == nRet);

        }

        public override bool SetLineStatus(IOLines line, LineStatus linestatus)
        {

            int nRet = device.Parameters.SetBoolValue(line.ToString(), linestatus.Equals(LineStatus.Hight));
            return (MvError.MV_OK == nRet);
        }

        public override bool GetLineStatus(IOLines line, out LineStatus linestatus)
        {
            bool resultsignal = false;
            int nRet = device.Parameters.GetBoolValue(line.ToString(), out resultsignal);
            linestatus = resultsignal ? LineStatus.Hight : LineStatus.Low;
            return (MvError.MV_OK == nRet);
        }

        /// <summary>
        /// 自动白平衡
        /// </summary>
        /// <returns></returns>
        public override bool AutoBalanceWhite()
        {
            int nRet = device.Parameters.SetEnumValueByString("BalanceWhiteAuto", "Once");
            return (MvError.MV_OK == nRet);
        }
        #endregion

        #region 帮助
        /// <summary>
        /// 显示错误信息
        /// </summary>
        /// <param name="message"></param>
        /// <param name="errorCode"></param>
        private void ShowErrorMsg(string message, int errorCode)
        {
            string errorMsg;
            if (errorCode == 0)
            {
                errorMsg = message;
            }
            else
            {
                errorMsg = message + ": Error =" + String.Format("{0:X}", errorCode);
            }

            switch (errorCode)
            {
                case MvError.MV_E_HANDLE: errorMsg += " 错误或无效句柄 "; break;
                case MvError.MV_E_SUPPORT: errorMsg += " 不支持的功能 "; break;
                case MvError.MV_E_BUFOVER: errorMsg += " 缓存已满 "; break;
                case MvError.MV_E_CALLORDER: errorMsg += " 函数调用顺序错误 "; break;
                case MvError.MV_E_PARAMETER: errorMsg += " 不正确的参数 "; break;
                case MvError.MV_E_RESOURCE: errorMsg += " 应用资源失败 "; break;
                case MvError.MV_E_NODATA: errorMsg += " 没有数据 "; break;
                case MvError.MV_E_PRECONDITION: errorMsg += " 前提条件错误，或运行环境已更改 "; break;
                case MvError.MV_E_VERSION: errorMsg += " 版本不匹配 "; break;
                case MvError.MV_E_NOENOUGH_BUF: errorMsg += " 内存不足 "; break;
                case MvError.MV_E_UNKNOW: errorMsg += " 未知错误 "; break;
                case MvError.MV_E_GC_GENERIC: errorMsg += " 一般错误 "; break;
                case MvError.MV_E_GC_ACCESS: errorMsg += " 一般错误 "; break;
                case MvError.MV_E_ACCESS_DENIED: errorMsg += " 没有权限 "; break;
                case MvError.MV_E_BUSY: errorMsg += " 设备正忙，或网络已断开连接 "; break;
                case MvError.MV_E_NETER: errorMsg += " 网络错误 "; break;
            }

            MessageBox.Show(errorMsg, "PROMPT");
        }

        /// <summary>
        /// 资源释放
        /// </summary>
        public override void Dispose()
        {
            if (device != null)
            {
                SN = string.Empty;
                device.Close();
                device.Dispose();
            }
        }

        /// <summary>
        /// 获取设备信息
        /// </summary>
        /// <returns></returns>
        private List<IDeviceInfo> GetListInfoEnum()
        {
            System.GC.Collect();
            deviceInfoList.Clear();
            // ch:创建设备列表 | en:Create Device List
            int nRet = DeviceEnumerator.EnumDevices(enumTLayerType, out deviceInfoList);
            if (nRet != MvError.MV_OK)
            {
                ShowErrorMsg("Enumerate devices fail!", nRet);
                return new List<IDeviceInfo>();
            }

            // ch:在窗体列表中显示设备名 | en:Display device name in the form list
            for (int i = 0; i < deviceInfoList.Count; i++)
            {
                IDeviceInfo deviceInfo = deviceInfoList[i];
                if (deviceInfo.UserDefinedName != "")
                {
                    deviceSNList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.UserDefinedName + " (" + deviceInfo.SerialNumber + ")");
                }
                else
                {
                    deviceSNList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.ManufacturerName + " " + deviceInfo.ModelName + " (" + deviceInfo.SerialNumber + ")");
                }
            }

            // ch:选择第一项 | en:Select the first item
            if (deviceInfoList.Count != 0)
            {
                deviceSNList[0] = deviceSNList[0];
            }
            return deviceInfoList;
        }

        /// <summary>
        /// 获取黑白像素格式
        /// </summary>
        /// <param name="enType"></param>
        /// <returns></returns>
        static bool IsMonoPixelFormat(MvGvspPixelType enType)
        {
            switch (enType)
            {
                case MvGvspPixelType.PixelType_Gvsp_Mono10:
                case MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
                case MvGvspPixelType.PixelType_Gvsp_Mono12:
                case MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// 获取彩色像素格式
        /// </summary>
        /// <param name="enType"></param>
        /// <returns></returns>
        static bool IsColorPixelFormat(MvGvspPixelType enType)
        {
            switch (enType)
            {
                case MvGvspPixelType.PixelType_Gvsp_BGR8_Packed:
                case MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
                case MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
                case MvGvspPixelType.PixelType_Gvsp_BayerGR8:
                case MvGvspPixelType.PixelType_Gvsp_BayerRG8:
                case MvGvspPixelType.PixelType_Gvsp_BayerGB8:
                case MvGvspPixelType.PixelType_Gvsp_BayerBG8:
                case MvGvspPixelType.PixelType_Gvsp_BayerGB10:
                case MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
                case MvGvspPixelType.PixelType_Gvsp_BayerBG10:
                case MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
                case MvGvspPixelType.PixelType_Gvsp_BayerRG10:
                case MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
                case MvGvspPixelType.PixelType_Gvsp_BayerGR10:
                case MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
                case MvGvspPixelType.PixelType_Gvsp_BayerGB12:
                case MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
                case MvGvspPixelType.PixelType_Gvsp_BayerBG12:
                case MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
                case MvGvspPixelType.PixelType_Gvsp_BayerRG12:
                case MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
                case MvGvspPixelType.PixelType_Gvsp_BayerGR12:
                case MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// 回调函数(不要在内部增加耗时操作)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CallBackEventHandler(object sender, FrameGrabbedEventArgs e)
        {
            int nRet;
            Debug.WriteLine("获取图像缓冲区: 宽度[{0}] , 高度[{1}] , 帧数[{2}]"
            , e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.FrameNum);

            if (isRecord) // 用于录像
            {
                device.VideoRecorder.InputOneFrame(e.FrameOut.Image); // 输入一帧图像-录像用的
            }

            lock (saveImageLock) // 用于保存图像
            {
                try
                {
                    frameForSave = e.FrameOut.Clone() as IFrameOut; // 克隆图像
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"IFrameOut.克隆失败,{ex.Message}");
                    return;
                }
            }

            IImage inputImage = e.FrameOut.Image;
            IImage outImage = inputImage;
            MvGvspPixelType dstPixelType = MvGvspPixelType.PixelType_Gvsp_Undefined;

            if (IsColorPixelFormat(e.FrameOut.Image.PixelType))
            {
                dstPixelType = MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
            }
            else if (IsMonoPixelFormat(e.FrameOut.Image.PixelType))
            {
                dstPixelType = MvGvspPixelType.PixelType_Gvsp_Mono8;
            }
            else
            {
                Trace.WriteLine("不需要转换!");
            }

            if (dstPixelType != MvGvspPixelType.PixelType_Gvsp_Undefined)
            {
                // ch:像素格式转换 | en:Pixel type convert 
                nRet = device.PixelTypeConverter.ConvertPixelType(inputImage, out outImage, dstPixelType);
                if (nRet != MvError.MV_OK)
                {
                    Console.WriteLine("图像转换失败:{0:x8}", nRet);
                    return;
                }
                Console.WriteLine("图像转换成功!");
            }
            //ch: 释放图像缓存 | en: Release image buffer
            device.StreamGrabber.FreeImageBuffer(e.FrameOut);
            // 调用对应托更新界面显示图像
            ActionGetImage?.Invoke(SN, outImage.ToBitmap());
        }
        #endregion
    }
}


